// Copyright (c) 2020-present, INSPUR Co, Ltd. All rights reserved.
// This source code is licensed under Apache 2.0 License.

// wangzekun01@inspur.com: 测试准内存引擎重启场景

#include <thread>
#include "db/db_test_util.h"
#include "port/stack_trace.h"
#include "rocksdb/perf_context.h"
#include "util/fault_injection_test_env.h"
#if !defined(ROCKSDB_LITE)
#include "util/sync_point.h"
#endif

namespace rocksdb{
class Level0CacheRecoverTest : public DBTestBase {
 public:
  Level0CacheRecoverTest() : DBTestBase("/db_basic_test") {
    last_options_.rocksdb_l0_cache_flush_l1 = true;
    Close();
    DB::Open(last_options_, dbname_, &db_);
  }
};

TEST_F(Level0CacheRecoverTest, BasicRecover) {
  for(int i = 0; i < 10; ++i){
    std::string key = "key" + ToString(i) + '\0';
    ASSERT_OK(Put(key, "c"));
  }
  std::string begin_del_key("key2");
  begin_del_key += '\0';
  std::string end_del_key("key5");
  end_del_key += '\0';
  db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), begin_del_key, end_del_key);
  std::string delete_key("key8");
  delete_key += '\0';
  ASSERT_OK(Delete(delete_key));
  ASSERT_EQ(Get("key" + ToString(0) + '\0'), "c");
  ASSERT_EQ(Get("key" + ToString(1) + '\0'), "c");
  ASSERT_EQ(Get("key" + ToString(2) + '\0'), "NOT_FOUND");
  ASSERT_EQ(Get("key" + ToString(3) + '\0'), "NOT_FOUND");
  ASSERT_EQ(Get("key" + ToString(4) + '\0'), "NOT_FOUND");
  ASSERT_EQ(Get("key" + ToString(5) + '\0'), "c");
  ASSERT_EQ(Get("key" + ToString(6) + '\0'), "c");
  ASSERT_EQ(Get("key" + ToString(7) + '\0'), "c");
  ASSERT_EQ(Get("key" + ToString(8) + '\0'), "NOT_FOUND");
  ASSERT_EQ(Get("key" + ToString(9) + '\0'), "c");

  Close();
  DB::Open(last_options_, dbname_, &db_);

  ASSERT_EQ(Get("key" + ToString(0) + '\0'), "c");
  ASSERT_EQ(Get("key" + ToString(1) + '\0'), "c");
  ASSERT_EQ(Get("key" + ToString(2) + '\0'), "NOT_FOUND");
//  ASSERT_EQ(Get("key" + ToString(3) + '\0'), "NOT_FOUND");
//  ASSERT_EQ(Get("key" + ToString(4) + '\0'), "NOT_FOUND");
  ASSERT_EQ(Get("key" + ToString(5) + '\0'), "c");
  ASSERT_EQ(Get("key" + ToString(6) + '\0'), "c");
  ASSERT_EQ(Get("key" + ToString(7) + '\0'), "c");
  ASSERT_EQ(Get("key" + ToString(8) + '\0'), "NOT_FOUND");
  ASSERT_EQ(Get("key" + ToString(9) + '\0'), "c");
}

TEST_F(Level0CacheRecoverTest, Level0SSTRecover) {
  for(int i = 0; i < 10000; ++i){
    std::string key = "key" + ToString(i) + '\0';
    ASSERT_OK(Put(key, std::string(1000, 'c')));
  }
  Close();
  DB::Open(last_options_, dbname_, &db_);

  for(int i = 0; i < 10000; ++i){
    std::string key = "key" + ToString(i) + '\0';
    ASSERT_EQ(Get(key), std::string(1000, 'c'));
  }
}

TEST_F(Level0CacheRecoverTest, RocksdbToL0CacheRecover) {
  Close();
  last_options_.rocksdb_l0_cache_flush_l1 = false;
  DB::Open(last_options_, dbname_, &db_);
  for(int i = 0; i < 30000; ++i){
    std::string key = "key" + ToString(i) + '\0';
    ASSERT_OK(Put(key, std::string(1000, 'c')));
  }

  for(int i = 0; i < 30000; ++i){
    std::string key = "key" + ToString(i) + '\0';
    ASSERT_EQ(Get(key), std::string(1000, 'c'));
  }

  Close();
  last_options_.rocksdb_l0_cache_flush_l1 = true;
  DB::Open(last_options_, dbname_, &db_);

  for(int i = 0; i < 30000; ++i){
    std::string key = "key" + ToString(i) + '\0';
    ASSERT_EQ(Get(key), std::string(1000, 'c'));
  }
}

TEST_F(Level0CacheRecoverTest, L0CacheToRocksdbRecover) {
  Close();
  last_options_.rocksdb_l0_cache_flush_l1 = true;
  DB::Open(last_options_, dbname_, &db_);
  for(int i = 0; i < 30000; ++i){
    std::string key = "key" + ToString(i) + '\0';
    ASSERT_OK(Put(key, std::string(1000, 'c')));
  }

  for(int i = 0; i < 30000; ++i){
    std::string key = "key" + ToString(i) + '\0';
    ASSERT_EQ(Get(key), std::string(1000, 'c'));
  }

  Close();
  last_options_.rocksdb_l0_cache_flush_l1 = false;
  DB::Open(last_options_, dbname_, &db_);

  for(int i = 0; i < 30000; ++i){
    std::string key = "key" + ToString(i) + '\0';
    ASSERT_EQ(Get(key), std::string(1000, 'c'));
  }
}


} // namespace rocksdb

int main(int argc, char **argv) {
  rocksdb::port::InstallStackTraceHandler();
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}